package co.baiku.boot.common.validate;

import co.baiku.boot.common.message.ErrorCode;
import co.baiku.boot.common.tools.AnnotationTools;
import co.baiku.boot.common.tools.ObjectTools;
import co.baiku.boot.common.tools.ReflectTools;
import co.baiku.boot.common.tools.StringTools;
import co.baiku.boot.common.validate.annotation.CheckField;
import co.baiku.boot.common.validate.annotation.CheckFields;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Map;

import static co.baiku.boot.common.validate.ValidationErrorType.*;

@SuppressWarnings("ALL")
public class Validation {
    private static ExpressionParser parser = new SpelExpressionParser();

    public static ValidationException error(String msg) {
        return new ValidationException(new ValidationResult(false, msg));
    }

    public static ValidationException error(ErrorCode errorCode) {
        return new ValidationException(new ValidationResult(false, errorCode.getMsg(), errorCode.getCode(), errorCode.getKey()));
    }

    public static boolean notNull(Object obj, String msg) {
        if (ObjectTools.isBlank(obj)) {
            throw error(msg);
        }
        return true;
    }

    public static <T> ValidationResult assertSuccess(T t) {
        ValidationResult exec = exec(t);
        if (exec.isSuccess()) {
            return exec;
        }
        throw new ValidationException(exec);
    }

    private static <T> ValidationResult ckeckField(T t, Map.Entry<Field, Annotation> fieldEntry, CheckField annotation) throws IllegalAccessException {
        ValidationResult result = new ValidationResult();
        Field field = fieldEntry.getKey();
        result.setFieldName(field.getName());
        result.setSuccess(false);
        Object value = ReflectTools.getValue(field, t);
        String script = annotation.expression();
        boolean flag;
        //表达式优先级最高
        if (StringTools.isNotBlank(script) && t != null) {
            flag = checkExpression(script, t);
            if (!flag) {
                result.setMsg(annotation.message());
                result.setErrorType(SCRIPT_DOES_NOT_MATCH);
                return result;
            }
        } else if (StringTools.isNotBlank(annotation.regex()) && value instanceof String) {//正则表达式
            flag = StringTools.regexMatch(value.toString(), annotation.regex());
            if (!flag) {
                result.setMsg(annotation.message());
                result.setErrorType(SCRIPT_DOES_NOT_MATCH);
                return result;
            }
        } else {
            //为空判断
            if (annotation.notEmpty() && ObjectTools.isBlank(value)) {
                result.setMsg(annotation.message());
                result.setErrorType(CAN_NOT_BE_EMPTY);
                return result;
            }
            //null判断
            if (annotation.notNull() && value == null) {
                result.setMsg(annotation.message());
                result.setErrorType(CAN_NOT_BE_NULL);
                return result;
            }
            //数值判断
            if (value instanceof Number) {
                Number num = ((Number) value);
                //小于
                if (StringTools.isNotBlank(annotation.lt())) {
                    Double target = Double.valueOf(annotation.lt());
                    if (target <= num.doubleValue()) {
                        result.setMsg(annotation.message());
                        result.setErrorType(LT);
                        return result;
                    }
                }
                //大于
                if (StringTools.isNotBlank(annotation.gt())) {
                    Double target = Double.valueOf(annotation.gt());
                    if (target >= num.doubleValue()) {
                        result.setMsg(annotation.message());
                        result.setErrorType(GT);
                        return result;
                    }
                }
                //小于等于
                if (StringTools.isNotBlank(annotation.le())) {
                    Double target = Double.valueOf(annotation.le());
                    if (target < num.doubleValue()) {
                        result.setMsg(annotation.message());
                        result.setErrorType(LTE);
                        return result;
                    }
                }
                //大于等于
                if (StringTools.isNotBlank(annotation.ge())) {
                    Double target = Double.valueOf(annotation.ge());
                    if (target > num.doubleValue()) {
                        result.setMsg(annotation.message());
                        result.setErrorType(GTE);
                        return result;
                    }
                }
            }
            //长度
            if (StringTools.isNotBlank(annotation.length()) && value instanceof String) {
                String target = annotation.length();
                if (target.contains("~") || target.contains(StringTools.COMMA) || target.contains("-")) {
                    String[] tar = target.split("~|,|-");
                    Integer min = Integer.valueOf(tar[0]);
                    Integer max = Integer.valueOf(tar[1]);
                    if (!(value.toString().length() >= min && value.toString().length() <= max)) {
                        result.setMsg(annotation.message());
                        result.setErrorType(LENGTH);
                        return result;
                    }
                } else {
                    Integer max = Integer.valueOf(target);
                    if (value.toString().length() > max) {
                        result.setMsg(annotation.message());
                        result.setErrorType(LENGTH);
                        return result;
                    }
                }
            }
        }
        result.setSuccess(true);
        return result;
    }

    public static <T> ValidationResult exec(T t) {
        return exec(t, true).first();
    }

    public static <T> ValidationResults execAll(T t) {
        return exec(t, false);
    }

    public static <T> ValidationResults exec(T t, boolean firstError) {
        ValidationResults result = new ValidationResults();
        if (t == null) {
            result.add(new ValidationResult(false, "待检查对象为null"));
            return result;
        }
        try {
            Map<Field, Annotation> checkFieldMap = AnnotationTools.findAllFieldAnnotation(t.getClass(), CheckField.class);
            for (Map.Entry<Field, Annotation> fieldEntry : checkFieldMap.entrySet()) {
                CheckField checkField = (CheckField) fieldEntry.getValue();
                ValidationResult item = ckeckField(t, fieldEntry, checkField);
                if (!item.isSuccess()) {
                    result.add(item);
                    if (firstError) {
                        break;
                    }
                }
            }
            Map<Field, Annotation> checkFieldsMap = AnnotationTools.findAllFieldAnnotation(t.getClass(), CheckFields.class);
            for (Map.Entry<Field, Annotation> fieldEntry : checkFieldsMap.entrySet()) {
                CheckFields annotation = (CheckFields) fieldEntry.getValue();
                CheckField[] checkFields = annotation.checkFields();
                for (CheckField checkField : checkFields) {
                    ValidationResult item = ckeckField(t, fieldEntry, checkField);
                    if (!item.isSuccess()) {
                        result.add(item);
                        if (firstError) {
                            break;
                        }
                    }
                }
            }
        } catch (IllegalAccessException e) {
            result.add(new ValidationResult(false, "读取对象属性异常：" + e.getMessage()));
        } catch (Exception e) {
            result.add(new ValidationResult(false, "验证表达式异常：" + e.getMessage()));
        }
        return result;
    }

    public static <T> boolean checkExpression(String expression, T t) {
        //把pojo对象传入脚本
        StandardEvaluationContext context = new StandardEvaluationContext(t);
        //执行表达式
        Expression result = parser.parseExpression(expression);
        Object eval = result.getValue(context);
        boolean flag = true;
        if (eval instanceof Boolean) {
            flag = (Boolean) eval;
        } else if (eval instanceof String) {
            flag = StringTools.isNotBlank(eval.toString()) && !((String) eval).equalsIgnoreCase("false");
        }
        return flag;
    }
}
